# +----------------------------------------------------------------------
# | ChatWork智能聊天办公系统
# +----------------------------------------------------------------------
# | 软件声明: 本系统并非自由软件,未经授权任何形式的商业使用均属非法。
# | 版权保护: 任何企业和个人不允许对程序代码以任何形式任何目的复制/分发。
# | 授权要求: 如有商业使用需求,请务必先与版权所有者取得联系并获得正式授权。
# +----------------------------------------------------------------------
# | Author: ChatWork Team <2474369941@qq.com>
# +----------------------------------------------------------------------
import json
import time
from typing import List
from pydantic import TypeAdapter
from tortoise.expressions import Q
from exception import AppException
from hypertext import PagingResult
from common.utils.times import TimeUtil
from common.utils.tools import ToolsUtil
from common.utils.config import ConfigUtil
from common.enums.pay import PayEnum
from common.models.users import UserSubscribeModel
from common.models.market import SubscribeOrderModel
from common.models.market import SubscribePackageModel
from common.models.market import SubscribePricingModel
from common.models.market import SubscribeSpecificsModel
from apps.api.schemas import subscribe_schema as schema
from common.service.subscribe_logic import SubscribeLogic


class SubscribeService:
    """ 会员订阅服务类 """

    @classmethod
    async def package(cls, user_id: int, calendar: str) -> schema.SubscribeListVo:
        """
        订阅套餐列表

        Args:
            user_id (int): 用户ID
            calendar (str): 时间类型

        Returns:
            schema.SubscribeListVo

        Author:
            zero
        """
        # 时间选项
        options = []
        choose = {"day": "按日", "month": "按月", "year": "按年", "perpetual": "永久"}
        timeline = await ConfigUtil.get("subscribe", "timeline") or {}
        for k, v in choose.items():
            if timeline.get(k):
                options.append(schema.SubscribeListVo.Options(
                    type=k,
                    label=v
                ))

        if not calendar and options:
            calendar = options[0].type

        if calendar not in ["day", "month", "year", "perpetual"]:
            raise AppException("calendar 参数异常")

        # 查询定价
        pricing_free = await SubscribePricingModel.filter(is_builtin=1).limit(1).all()
        pricing_list = pricing_free + await (SubscribePricingModel
                                             .filter(calendar=calendar)
                                             .filter(is_builtin=0)
                                             .filter(is_disable=0)
                                             .filter(is_delete=0)
                                             .order_by("-is_builtin", "sort", "id")
                                             .group_by("package_id")
                                             .all())

        # 查询套餐
        package_ids = list(set([item.package_id for item in pricing_list]))
        packages = await (SubscribePackageModel
                          .filter(id__in=package_ids)
                          .filter(is_disable=0)
                          .filter(is_delete=0)
                          .order_by("-is_builtin", "-sort")
                          .all().values("id", "name", "description", "is_builtin", "is_disable"))
        package_dict = {item["id"]: item for item in packages}

        # 套餐列表
        _lists = []
        for item in pricing_list:
            package = package_dict.get(item.package_id, None)
            if package:
                specifics = await (SubscribeSpecificsModel
                                   .filter(package_id=item.package_id)
                                   .filter(is_disable=0)
                                   .order_by("sort")
                                   .all().values("name", "description", "is_support"))

                _specifics = []
                for sp in specifics:
                    name = sp["name"].replace("{{give_points}}", str(item.give_points))
                    description = sp["description"].replace("{{give_points}}", str(item.give_points))
                    _specifics.append(schema.SubscribeSpecificVo(
                        name=name,
                        description=description,
                        is_support=sp["is_support"]
                    ))

                _lists.append(schema.SubscribePackageVo(
                    id=package["id"],
                    pricing_id=item.id,
                    name=package["name"],
                    tags=item.tags,
                    description=package["description"],
                    sell_price=item.sell_price,
                    line_price=item.line_price,
                    my_version=0,
                    is_purchase=1,
                    is_builtin=package["is_builtin"],
                    is_disable=package["is_disable"],
                    specifics=_specifics
                ))

        # 从新排序
        index_m = {pid: idx for idx, pid in enumerate(package_ids)}
        sorted_list = sorted(_lists, key=lambda member: index_m[member.id])

        # 处理状态
        userMember = await SubscribeLogic.get_user_vip(user_id=user_id)
        for item in sorted_list:
            if item.id == userMember["id"]:
                item.my_version = 1
                break
            item.is_purchase = 0

        # 返回结果
        return schema.SubscribeListVo(
            scene=calendar,
            option=options,
            lists=sorted_list
        )

    @classmethod
    async def place(cls, user_id: int, terminal: int, post: schema.SubscribePlaceIn) -> schema.SubscribePlaceVo:
        """
        订阅下单

        Args:
            user_id (int): 用户ID。
            terminal (int): 操作平台。
            post (schema.SubscribePlaceIn): 订阅下单参数。

        Returns:
            schema.RechargePlaceVo: 下单结果Vo。

        Author:
            zero
        """
        status = int(await ConfigUtil.get("subscribe", "status", 0))
        if not status:
            raise AppException("订阅通道已关闭")

        # 查询定价
        pricing = await SubscribePricingModel.filter(id=post.package_id, is_delete=0).first()
        if not pricing:
            raise AppException("套餐不存在")
        if pricing.is_disable:
            raise AppException("套餐已下架")

        # 查询套餐
        package = await SubscribePackageModel.filter(id=pricing.package_id, is_delete=0).first()
        if not package:
            raise AppException("套餐不存在")
        if package.is_disable:
            raise AppException("套餐已下架")

        # 验证套餐
        userMember = await SubscribeLogic.get_user_vip(user_id=user_id)
        if userMember:
            # 排除比自己套餐低的套餐
            packageIds = await SubscribePackageModel.filter(is_delete=0).order_by("sort", "id").values("id")
            packageIds = [item["id"] for item in packageIds]
            index = 0
            for i in packageIds:
                if i == userMember["id"]:
                    break
                else:
                    index += 1

            packageIds = packageIds[index:]

            # 验证套餐是否可进行开通
            if pricing.package_id not in packageIds:
                raise AppException("抱歉,您无法订阅比当前套餐低的套餐")

        # 创建订单
        order = await SubscribeOrderModel.create(
            user_id=user_id,
            terminal=terminal,
            order_sn=await ToolsUtil.make_order_sn(SubscribeOrderModel, "order_sn"),
            pay_way=PayEnum.WAY_MNP,
            package_id=package.id,
            pricing_id=pricing.id,
            order_amount=pricing.sell_price,
            paid_amount=pricing.sell_price,
            create_time=int(time.time()),
            update_time=int(time.time()),
            snapshot=json.dumps({
                "name": package.name,
                "description": package.description,
                "train_num": package.train_num,
                "robot_num": package.robot_num,
                "know_num": package.know_num,
                "sort": package.sort,
                "tags": pricing.tags,
                "calendar": pricing.calendar,
                "duration": pricing.duration,
                "give_points": pricing.give_points,
                "sell_price": str(pricing.sell_price),
                "line_price": str(pricing.line_price)
            }, ensure_ascii=False)
        )

        return schema.SubscribePlaceVo(
            order_id=order.id,
            paid_amount=pricing.sell_price
        )

    @classmethod
    async def billing(cls, user_id: int, params: schema.BillSearchIn) -> PagingResult[schema.SubscribeBillingVo]:
        """
        订阅账单

        Args:
            user_id (int): 用户ID。
            params (schema.BillSearchIn): 搜索参数。

        Returns:
            PagingResult[schema.SubscribeBillingVo]: 分页账单Vo。

        Author:
            zero
        """
        # 查询条件
        where = SubscribeOrderModel.build_search({
            "datetime": ["start_time|end_time@create_time"]
        }, params.__dict__)

        where.append(Q(user_id=user_id))
        where.append(Q(pay_status=PayEnum.PAID_OK))

        # 查询数据
        _model = SubscribeOrderModel.filter(*where).order_by("-id", "-pay_time")
        _pager = await SubscribeOrderModel.paginate(
            model=_model,
            page_no=params.page_no,
            page_size=params.page_size,
            datetime_field=["pay_time", "create_time", "update_time", "package_end_time"],
        )

        # 处理数据
        _lists = []
        for item in _pager.lists:
            snapshot = json.loads(item["snapshot"] or "{}")
            _lists.append(schema.SubscribeBillingVo(
                order_sn=item["order_sn"],
                pay_way=PayEnum.get_pay_way_msg(item["pay_way"]),
                pay_status=PayEnum.get_pay_status_msg(item["pay_status"]),
                package_name=snapshot.get("name", "-"),
                order_amount=item["order_amount"],
                paid_amount=item["paid_amount"],
                package_end_time=item["package_end_time"],
                pay_time=item["pay_time"],
                create_time=item["create_time"]
            ))

        _pager.lists = _lists
        return _pager

    @classmethod
    async def record(cls, user_id: int) -> List[schema.SubscribeRecordVo]:
        """
        订阅记录 (生效中/待生效)

        Args:
            user_id (int): 用户ID。

        Returns:
            List[schema.SubscribeRecordVo]: 记录列表VO。

        Author:
            zero
        """
        await SubscribeLogic.get_user_vip(user_id=1)
        lists = await (UserSubscribeModel
                       .filter(user_id=user_id)
                       .filter(is_delete=0)
                       .filter(expire_time__gt=int(time.time()))
                       .order_by("effect_time")
                       .all())

        package_ids = [item.package_id for item in lists]
        package_arr = await SubscribePackageModel.filter(id__in=package_ids).values("id", "name")
        packages = {item["id"]: item["name"] for item in package_arr}

        _lists = []
        for index, item in enumerate(lists):
            _lists.append(schema.SubscribeRecordVo(
                name=packages.get(item.package_id, "-"),
                is_effect=True if index == 0 else False,
                is_perpetual=bool(item.is_perpetual),
                effect_time=TimeUtil.timestamp_to_date(item.effect_time),
                expire_time=TimeUtil.timestamp_to_date(item.expire_time)
            ))

        return _lists
